﻿
using CSharpLibs.Annotations;

namespace System
{
	[Builtin]
	public struct DateTime : IComparable<DateTime>, IEquatable<DateTime>
	{
		private static readonly int[] DaysToMonth365;
		private static readonly int[] DaysToMonth366;

		private ulong dateData;

		static DateTime()
		{
			DaysToMonth365 = new int[13];
			DaysToMonth365[0] = 0;
			DaysToMonth365[1] = 0x1f;
			DaysToMonth365[2] = 0x3b;
			DaysToMonth365[3] = 90;
			DaysToMonth365[4] = 120;
			DaysToMonth365[5] = 0x97;
			DaysToMonth365[6] = 0xb5;
			DaysToMonth365[7] = 0xd4;
			DaysToMonth365[8] = 0xf3;
			DaysToMonth365[9] = 0x111;
			DaysToMonth365[10] = 0x130;
			DaysToMonth365[11] = 0x14e;
			DaysToMonth365[12] = 0x16d;

			DaysToMonth366 = new int[13];
			DaysToMonth366[0] = 0;
			DaysToMonth366[1] = 0x1f;
			DaysToMonth366[2] = 60;
			DaysToMonth366[3] = 0x5b;
			DaysToMonth366[4] = 0x79;
			DaysToMonth366[5] = 0x98;
			DaysToMonth366[6] = 0xb6;
			DaysToMonth366[7] = 0xd5;
			DaysToMonth366[8] = 0xf4;
			DaysToMonth366[9] = 0x112;
			DaysToMonth366[10] = 0x131;
			DaysToMonth366[11] = 0x14f;
			DaysToMonth366[12] = 0x16e;
		}

		public DateTime(long ticks)
		{ this.dateData = (ulong)ticks; }

		private DateTime(ulong dateData)
		{ this.dateData = dateData; }

		public DateTime(int year, int month, int day)
		{ this.dateData = (ulong)DateToTicks(year, month, day); }

		public DateTime Add(TimeSpan ts)
		{ return this.AddTicks(ts._ticks); }

		private DateTime Add(double value, int scale)
		{
			long num = (long)((value * scale) + ((value >= 0.0) ? 0.5 : -0.5));
			return this.AddTicks(num * 0x2710L);
		}

		public DateTime AddDays(double value)
		{ return this.Add(value, 0x5265c00); }

		public DateTime AddHours(double value)
		{ return this.Add(value, 0x36ee80); }

		public DateTime AddMilliseconds(double value)
		{ return this.Add(value, 1); }

		public DateTime AddMinutes(double value)
		{ return this.Add(value, 0xea60); }

		public DateTime AddMonths(int months)
		{
			int datePart = this.GetDatePart(0);
			int month = this.GetDatePart(2);
			int day = this.GetDatePart(3);
			int num4 = (month - 1) + months;
			if(num4 >= 0)
			{
				month = (num4 % 12) + 1;
				datePart += num4 / 12;
			}
			else
			{
				month = 12 + ((num4 + 1) % 12);
				datePart += (num4 - 11) / 12;
			}
			
			int num5 = DaysInMonth(datePart, month);
			if(day > num5)
			{
				day = num5;
			}
			
			return new DateTime(((ulong)(DateToTicks(datePart, month, day) + (this.InternalTicks % 0xc92a69c000L))) | this.InternalKind);
		}

		public DateTime AddSeconds(double value)
		{ return this.Add(value, 0x3e8); }

		public DateTime AddTicks(long value)
		{
			long internalTicks = this.InternalTicks;
			return new DateTime(((ulong)(internalTicks + value)) | this.InternalKind);
		}

		public DateTime AddYears(int value)
		{ return this.AddMonths(value * 12); }

		public int CompareTo(DateTime value)
		{
			long internalTicks = value.InternalTicks;
			long num2 = this.InternalTicks;
			if(num2 > internalTicks)
			{
				return 1;
			}
			if(num2 < internalTicks)
			{
				return -1;
			}
			return 0;
		}

		private static long DateToTicks(int year, int month, int day)
		{
			if(((year >= 1) && (year <= 0x270f)) && ((month >= 1) && (month <= 12)))
			{
				int[] numArray = IsLeapYear(year) ? DaysToMonth366 : DaysToMonth365;
				if((day >= 1) && (day <= (numArray[month] - numArray[month - 1])))
				{
					int num = year - 1;
					int num2 = ((((((num * 0x16d) + (num / 4)) - (num / 100)) + (num / 400)) + numArray[month - 1]) + day) - 1;
					return (num2 * 0xc92a69c000L);
				}
			}

			Environment.Exit(1);
			return -1;
		}

		public static int DaysInMonth(int year, int month)
		{
			int[] numArray = IsLeapYear(year) ? DaysToMonth366 : DaysToMonth365;
			return (numArray[month] - numArray[month - 1]);
		}

		public override bool Equals(object value)
		{
			if(value is DateTime)
			{
				DateTime time = (DateTime)value;
				return (this.InternalTicks == time.InternalTicks);
			}
			return false;
		}

		public bool Equals(DateTime value)
		{
			return (this.InternalTicks == value.InternalTicks);
		}

		public override int GetHashCode()
		{
			long internalTicks = this.InternalTicks;
			return (((int)internalTicks) ^ ((int)(internalTicks >> 0x20)));
		}

		private int GetDatePart(int part)
		{
			int num2 = (int)(this.InternalTicks / 0xc92a69c000L);
			int num3 = num2 / 0x23ab1;
			num2 -= num3 * 0x23ab1;
			int num4 = num2 / 0x8eac;
			if(num4 == 4)
			{
				num4 = 3;
			}
			num2 -= num4 * 0x8eac;
			int num5 = num2 / 0x5b5;
			num2 -= num5 * 0x5b5;
			int num6 = num2 / 0x16d;
			if(num6 == 4)
			{
				num6 = 3;
			}
			if(part == 0)
			{
				return (((((num3 * 400) + (num4 * 100)) + (num5 * 4)) + num6) + 1);
			}
			num2 -= num6 * 0x16d;
			if(part == 1)
			{
				return (num2 + 1);
			}
			int[] numArray = ((num6 == 3) && ((num5 != 0x18) || (num4 == 3))) ? DaysToMonth366 : DaysToMonth365;
			int index = num2 >> 6;
			while(num2 >= numArray[index])
			{
				index++;
			}
			if(part == 2)
			{
				return index;
			}
			return ((num2 - numArray[index - 1]) + 1);
		}

		public static bool IsLeapYear(int year)
		{
			if((year < 1) || (year > 0x270f))
				Environment.Exit(1);

			if((year % 4) != 0)
				return false;
			
			if((year % 100) == 0)
				return ((year % 400) == 0);

			return true;
		}

		public DateTime Subtract(TimeSpan value)
		{
			long internalTicks = this.InternalTicks;
			long num2 = value._ticks;
			return new DateTime(((ulong)(internalTicks - num2)) | this.InternalKind);
		}

		public TimeSpan Subtract(DateTime value)
		{
			return new TimeSpan(this.InternalTicks - value.InternalTicks);
		}

		public long ToFileTime()
		{
			return this.ToUniversalTime().ToFileTimeUtc();
		}

		[BuiltinMethod, BuiltinSemantics("datetime_tofileutc")]
		public long ToFileTimeUtc()
		{ return -1; }

		[BuiltinMethod, BuiltinSemantics("datetime_to_local")]
		public DateTime ToLocalTime()
		{ return new DateTime(0); }

		[BuiltinMethod, BuiltinSemantics("datetime_to_universal")]
		public DateTime ToUniversalTime()
		{ return new DateTime(0); }

		internal long InternalTicks
		{ get { return (((long)this.dateData) & 0x3fffffffffffffffL); } }

		private ulong InternalKind
		{
			[BuiltinMethod, BuiltinSemantics("datetime_internalkind")]
			get { return 0; } 
		}

		public int Day
		{ get { return this.GetDatePart(3); } }

		public int Hour
		{ get { return (int)((this.InternalTicks / 0x861c46800L) % 0x18L); } }

		public int Millisecond
		{ get { return (int)((this.InternalTicks / 0x2710L) % 0x3e8L); } }

		public int Minute
		{ get { return (int)((this.InternalTicks / 0x23c34600L) % 60L); } }

		public int Month
		{ get { return this.GetDatePart(2); } }

		public static DateTime Now
		{
			[BuiltinMethod, BuiltinSemantics("datetime_now")]
			[UsedModStatics("System.ExternalState.fsstate")]
			get { return new DateTime(0); }
		}

		public int Second
		{ get { return (int)((this.InternalTicks / 0x989680L) % 60L); } }

		public long Ticks
		{ get { return this.InternalTicks; } }

		public int Year
		{ get { return this.GetDatePart(0); } }
	}
}
